package com.ssy.lingxi.platform.manage.serviceimpl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.common.utils.PinyinUtils;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.platform.manage.api.model.dto.area.AreaAddDTO;
import com.ssy.lingxi.platform.manage.api.model.dto.area.AreaDTO;
import com.ssy.lingxi.platform.manage.api.model.vo.area.AreaVO;
import com.ssy.lingxi.platform.manage.api.model.vo.area.CityVO;
import com.ssy.lingxi.platform.manage.api.model.vo.area.LocationVO;
import com.ssy.lingxi.platform.manage.api.model.vo.area.ProvinceVO;
import com.ssy.lingxi.platform.manage.constant.CommonBooleanEnum;
import com.ssy.lingxi.platform.manage.entity.base.Area;
import com.ssy.lingxi.platform.manage.entity.base.PlatformParameterManage;
import com.ssy.lingxi.platform.manage.model.bo.RegionalInitialsBO;
import com.ssy.lingxi.platform.manage.model.response.RegionalInitialsResponse;
import com.ssy.lingxi.platform.manage.repository.AreaRepository;
import com.ssy.lingxi.platform.manage.repository.PlatformParameterManageRepository;
import com.ssy.lingxi.platform.manage.service.IAreaService;
import com.ssy.lingxi.platform.manage.util.HttpUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wrc
 * @version 2.0.0
 * @menu 省市区街道四级联动管理 - 业务实现层
 * @date 2020/7/20
 */
@Service
public class AreaServiceImpl implements IAreaService {

    @Resource
    private AreaRepository areaRepository;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private PlatformParameterManageRepository platformParameterManageRepository;

    /**
     * 根据省编码获取所有市编码
     */
    @Override
    public Wrapper<List<String>> findCityCodeByProvinceCode(List<String> provinceCodes) {
        if (CollectionUtil.isEmpty(provinceCodes)) {
            return Wrapper.fail(ResponseCode.PT_PARAMETER_VERIFICATION_FAILED);
        }

        List<Area> areas = areaRepository.findAllByPcodeIn(provinceCodes);
        return Wrapper.success(
                areas.stream().map(Area::getCode).collect(Collectors.toList())
        );
    }

    /**
     * 获取省市
     */
    @Override
    public Wrapper<List<ProvinceVO>> findProvinceCity() {
        List<Area> province = areaRepository.findAllByLevel(1);
        List<Area> city = areaRepository.findAllByLevel(2);

        List<ProvinceVO> list = province.stream().map(a -> {
            ProvinceVO provinceResponse = new ProvinceVO();
            provinceResponse.setProvinceName(a.getName());
            provinceResponse.setProvinceCode(a.getCode());
            provinceResponse.setCityList(
                    city.stream().filter(b -> b.getPcode().equals(a.getCode())).map(c -> {
                        CityVO cityResponse = new CityVO();
                        cityResponse.setCityName(c.getName());
                        cityResponse.setCityCode(c.getCode());
                        return cityResponse;
                    }).collect(Collectors.toList())
            );
            return provinceResponse;
        }).collect(Collectors.toList());

        return Wrapper.success(list);
    }

    /**
     * 查询省市区列表
     */
    @Override
    public Wrapper<List<AreaVO>> areaAll() {
        List<Area> all = areaRepository.findAll();
        List<AreaVO> list = new ArrayList<>();
        all.forEach(area -> {
            AreaVO areaResponse = new AreaVO();
            BeanUtils.copyProperties(area, areaResponse);
            list.add(areaResponse);
        });
        Map<String, List<AreaVO>> map = list.stream().collect(Collectors.groupingBy(AreaVO::getPcode));
        List<AreaVO> areas = map.get("100000");
        areas.forEach(area -> {
            getAreaResponses(map, area.getCode(), area);
        });
        return Wrapper.success(areas);
    }

    /**
     * 查询省市区列表
     */
    @Override
    public Wrapper<List<AreaVO>> areaList() {
        List<Area> all = areaRepository.findAll();
        List<AreaVO> list = new ArrayList<>();
        all.forEach(area -> {
            AreaVO areaResponse = new AreaVO();
            BeanUtils.copyProperties(area, areaResponse);
            list.add(areaResponse);
        });
        return Wrapper.success(list);
    }

    /**
     * 根据父code查询省市区列表
     */
    @Override
    public Wrapper<List<AreaVO>> areaByPcodeAll(AreaDTO request) {
        List<Area> allByPcodeEquals = areaRepository.findAllByPcodeEquals(request.getPcode());
        List<AreaVO> areaResponses = new ArrayList<>();
        if (null != allByPcodeEquals && allByPcodeEquals.size() > 0) {
            areaResponses = allByPcodeEquals.stream().map(e -> {
                AreaVO companyListResponse = new AreaVO();
                BeanUtils.copyProperties(e, companyListResponse);
                return companyListResponse;
            }).collect(Collectors.toList());
        }
        return Wrapper.success(areaResponses);
    }

    /**
     * 根据父code查询下级区域
     *
     * @param request 请求参数
     * @return 返回结果
     */
    @Override
    public Wrapper<List<Area>> areaByPcode(AreaDTO request) {
        if (StrUtil.isEmpty(request.getPcode())) {
            //pcode为空默认返回所有省份
            return Wrapper.success(areaRepository.findAllByLevel(1));
        }
        return Wrapper.success(areaRepository.findAllByPcode(request.getPcode()));
    }

    /**
     * 新增修改省市区
     *
     * @param areaAddRequest 省市区实体
     * @return 省市区id
     */
    @Override
    public Long saveOrUpdateArea(AreaAddDTO areaAddRequest) {
        String code = areaAddRequest.getCode();
        Area area = areaRepository.findOneByCode(code);
        if (area != null) {
            area.setLevel(areaAddRequest.getLevel());
            area.setName(areaAddRequest.getName());
            area.setPcode(areaAddRequest.getPcode());
        } else {
            area = BeanUtil.copyProperties(areaAddRequest, Area.class);
        }
        areaRepository.saveAndFlush(area);
        return area.getId();
    }

    /**
     * 初始化数据到redis
     */
    @Async
    @Override
    public void initToRedis() {
        redisStringUtils.del(Constants.REDIS_KEY_AREA, Constants.REDIS_PLATFORM_MANAGE_INDEX);
        List<Area> areaList = areaRepository.findAll();
        if (!areaList.isEmpty()) {
            Map<String, String> map = new HashMap<>();
            areaList.forEach(area -> map.put(String.valueOf(area.getCode()), JSONUtil.toJsonStr(area)));
            redisStringUtils.hMSet(Constants.REDIS_KEY_AREA, map, Constants.REDIS_PLATFORM_MANAGE_INDEX);
        }
    }

    /**
     * 缓存地区的同时缓存区域首字母
     */
    @Async
    @Override
    public void initRegionalInitials() {
        redisStringUtils.del(Constants.REDIS_KEY_REGIONAL_INITIALS, Constants.REDIS_PLATFORM_MANAGE_INDEX);
        List<Area> areaList = areaRepository.findAll();
        if (!areaList.isEmpty()) {
            List<RegionalInitialsBO> initialsBOList = areaList.stream().map(a -> {
                RegionalInitialsBO bo = new RegionalInitialsBO();
                BeanUtils.copyProperties(a, bo);
                bo.setFirstName(PinyinUtils.getFirstChar(a.getName()));
                return bo;
            }).collect(Collectors.toList());
            String initialsBOStr = JSONObject.toJSONString(initialsBOList);
            redisStringUtils.set(Constants.REDIS_KEY_REGIONAL_INITIALS, initialsBOStr, Constants.REDIS_PLATFORM_MANAGE_INDEX);
        }
    }

    /**
     * 递归查询省市区列表
     */
    private void getAreaResponses(Map<String, List<AreaVO>> collect, String code, AreaVO areaResponse) {
        List<AreaVO> areaResponseList = collect.get(code);
        if (null != areaResponseList && areaResponseList.size() > 0) {
            areaResponseList.forEach(area -> {
                areaResponse.setAreaResponses(areaResponseList);
                getAreaResponses(collect, area.getCode(), area);
            });
        }
    }


    /**
     * 根据ip获取城市信息
     *
     * @param code   用于定位的API的应用的ID的code
     * @param ipAddr ip地址
     * @return 操作结果
     */
    @Override
    public Wrapper<LocationVO> findCityByIP(String code, String ipAddr) {
        PlatformParameterManage parameterManage = platformParameterManageRepository.findByCode(code);
        if (parameterManage == null) {
            return Wrapper.fail(ResponseCode.MAN_RECORDS_DON_T_EXIST);
        }
        //请求参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("ip", ipAddr);
        paramMap.put("key", parameterManage.getParameterValue());
        //创建工具类对象 ip地址不会轻易改变
        HttpUtil net = new HttpUtil("https://restapi.amap.com/v3/ip", paramMap);
        //按照GET方法初始化
        net.init(HttpUtil.METHOD.GET);
        //拿到结果
        String result = net.result();
        JSONObject resp = JSON.parseObject(result);
        if (resp != null && CommonBooleanEnum.YES.getCode() == resp.getIntValue("status")) {
            LocationVO vo = new LocationVO();
            String cityCode = resp.getString("adcode");
            vo.setStatus(resp.getIntValue("status"));
            vo.setProvince(resp.getString("province"));
            vo.setCityName(resp.getString("city"));
            vo.setCityCode(cityCode);
            vo.setRectangle(resp.getString("rectangle"));
            //获取省code
            Map<String, String> areaMap = redisStringUtils.hGetAll(Constants.REDIS_KEY_AREA, Constants.REDIS_PLATFORM_MANAGE_INDEX);
            String areaStr = areaMap.get(cityCode);
            if (StringUtils.isNotBlank(areaStr)) {
                Area area = JSONObject.parseObject(areaStr, Area.class);
                vo.setProvinceCode(area.getPcode());
            }
            return Wrapper.success(vo);
        }
        return Wrapper.fail(ResponseCode.PLATFORM_MANAGE_LOCATION_INFORMATION_DOES_NOT_EXIST);
    }

    /**
     * 根据经纬度获取城市信息
     *
     * @param code     用于定位的API的应用的ID的code
     * @param location 请求数据 格式: 经度,纬度 -> 110.27321,21.60022
     * @return 操作结果
     */
    @Override
    public Wrapper<LocationVO> findByLocation(String code, String location) {
        PlatformParameterManage parameterManage = platformParameterManageRepository.findByCode(code);
        if (parameterManage == null) {
            return Wrapper.fail(ResponseCode.MAN_RECORDS_DON_T_EXIST);
        }
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("location", location);
        paramMap.put("key", parameterManage.getParameterValue());
        //创建工具类对象 ip地址不会轻易改变
        HttpUtil net = new HttpUtil("https://restapi.amap.com/v3/geocode/regeo", paramMap);
        //按照GET方法初始化
        net.init(HttpUtil.METHOD.GET);
        //拿到结果
        String result = net.result();
        JSONObject resp = JSON.parseObject(result);
        if (resp != null && CommonBooleanEnum.YES.getCode() == resp.getIntValue("status")) {
            LocationVO vo = new LocationVO();
            JSONObject reGeoCode = resp.getJSONObject("regeocode");
            JSONObject addressComponent = reGeoCode.getJSONObject("addressComponent");
            //获取县code
            String adCode = addressComponent.getString("adcode");
            //根据县code获取地级市code
            Map<String, String> areaMap = redisStringUtils.hGetAll(Constants.REDIS_KEY_AREA, Constants.REDIS_PLATFORM_MANAGE_INDEX);
            String cityStr = areaMap.get(adCode);
            if (StringUtils.isNotBlank(cityStr)) {
                Area cityArea = JSONObject.parseObject(cityStr, Area.class);
                vo.setCityName(addressComponent.getString("city"));
                vo.setCityCode(cityArea.getPcode());
                //获取省code
                String proStr = areaMap.get(cityArea.getPcode());
                if (StringUtils.isNotBlank(proStr)) {
                    Area proArea = JSONObject.parseObject(proStr, Area.class);
                    vo.setProvince(addressComponent.getString("province"));
                    vo.setProvinceCode(proArea.getPcode());
                }
            }
            vo.setStatus(resp.getIntValue("status"));
            return Wrapper.success(vo);
        }
        return Wrapper.fail(ResponseCode.PLATFORM_MANAGE_LOCATION_INFORMATION_DOES_NOT_EXIST);
    }

    /**
     * 根据code查询下级区域，不传参数默认获取所有省份 (返回的参数带区域首字母)
     *
     * @param request 请求参数
     * @return 操作结果
     */
    @Override
    public Wrapper<List<RegionalInitialsResponse>> findByPCode(AreaDTO request) {
        //从redis中获取地区数据
        String initialsBOStr = redisStringUtils.get(Constants.REDIS_KEY_REGIONAL_INITIALS, Constants.REDIS_PLATFORM_MANAGE_INDEX);
        //缓存没数据时从数据库中获取
        if (StringUtils.isBlank(initialsBOStr)) {
            List<Area> areaList;
            if (StrUtil.isEmpty(request.getPcode())) {
                areaList = areaRepository.findAllByLevel(1);
            } else {
                areaList = areaRepository.findAllByPcode(request.getPcode());
            }
            if (CollectionUtils.isEmpty(areaList)) {
                return Wrapper.success(new ArrayList<>());
            }
            //封装数据
            List<RegionalInitialsResponse> responseList = areaList.stream().map(a -> {
                RegionalInitialsResponse response = new RegionalInitialsResponse();
                BeanUtils.copyProperties(a, response);
                response.setFirstName(PinyinUtils.getFirstChar(a.getName()));
                return response;
            }).collect(Collectors.toList());
            return Wrapper.success(responseList);
        } else {
            //从redis中获取数据
            //pcode为空默认返回所有省份
            if (StrUtil.isEmpty(request.getPcode())) {
                List<RegionalInitialsResponse> responseList = JSONObject.parseArray(initialsBOStr, RegionalInitialsResponse.class);
                return Wrapper.success(responseList.stream().filter(r -> 1 == r.getLevel()).collect(Collectors.toList()));
            } else {
                return Wrapper.success(JSONObject.parseArray(initialsBOStr, RegionalInitialsResponse.class).stream().filter(i -> request.getPcode().equals(i.getPcode())).collect(Collectors.toList()));
            }
        }
    }

    @Override
    public Wrapper<List<ProvinceVO>> findProCityFirstName() {
        List<Area> province = areaRepository.findAllByLevel(1);
        List<Area> city = areaRepository.findAllByLevel(2);

        List<ProvinceVO> list = province.stream().map(a -> {
            ProvinceVO provinceResponse = new ProvinceVO();
            provinceResponse.setProvinceName(a.getName());
            provinceResponse.setProvinceCode(a.getCode());
            provinceResponse.setCityList(
                    city.stream().filter(b -> b.getPcode().equals(a.getCode())).map(c -> {
                        CityVO cityResponse = new CityVO();
                        cityResponse.setCityName(c.getName());
                        cityResponse.setCityCode(c.getCode());
                        cityResponse.setFirstName(PinyinUtils.getFirstChar(c.getName()));
                        return cityResponse;
                    }).collect(Collectors.toList())
            );
            return provinceResponse;
        }).collect(Collectors.toList());

        return Wrapper.success(list);
    }
}
